export rich version information from cargo::version
authorNathan Froyd <froydnj@gmail.com>
Fri, 27 Jan 2017 16:39:17 +0000 (11:39 -0500)
committerNathan Froyd <froydnj@gmail.com>
Fri, 27 Jan 2017 17:25:42 +0000 (12:25 -0500)
To support `cargo --version --verbose`, ala rustc, we need more
information to be injected into cargo when it's built from the Makefile,
and a more explicit data structure to be returned from cargo::version.
We implement fmt::Display for our newly-created structure so clients
don't have to bother with the details of interpreting the structure if
all they want is a string.

Makefile.in
src/cargo/lib.rs [changed mode: 0644->0755]
src/cargo/ops/registry.rs

index f4862903678b27d6cbfeb9607e8896be8a3bb358..4a410d6d4a3a55bf0d8fce7a9943b2bd7d6beda3 100644 (file)
@@ -25,12 +25,10 @@ endif
 # allow build systems to use a constant date instead of the current one
 CFG_BUILD_DATE = $(shell SOURCE_DATE_EPOCH="$${SOURCE_DATE_EPOCH:-$$(date +%s)}" ; date -u -d "@$$SOURCE_DATE_EPOCH" +%F 2>/dev/null || date -u -r "$$SOURCE_DATE_EPOCH" +%F 2>/dev/null || date -u +%F)
 
-ifeq ($(wildcard $(CFG_SRC_DIR)/.git),)
-CFG_VERSION = $(CFG_RELEASE) (built $(CFG_BUILD_DATE))
-else
-CFG_VER_DATE = $(shell git --git-dir='$(CFG_SRC_DIR).git' log -1 --date=short --pretty=format:'%cd')
-CFG_VER_HASH = $(shell git --git-dir='$(CFG_SRC_DIR).git' rev-parse --short HEAD)
-CFG_VERSION = $(CFG_RELEASE) ($(CFG_VER_HASH) $(CFG_VER_DATE))
+ifneq ($(wildcard $(CFG_SRC_DIR)/.git),)
+CFG_COMMIT_DATE = $(shell git --git-dir='$(CFG_SRC_DIR).git' log -1 --date=short --pretty=format:'%cd')
+CFG_SHORT_COMMIT_HASH = $(shell git --git-dir='$(CFG_SRC_DIR).git' rev-parse --short HEAD)
+CFG_COMMIT_HASH = $(shell git --git-dir='$(CFG_SRC_DIR).git' rev-parse HEAD)
 endif
 PKG_NAME = cargo-$(CFG_PACKAGE_VERS)
 
@@ -58,7 +56,25 @@ endif
 
 S := $(CFG_SRC_DIR)/
 
+CFG_RELEASE_PARTS := $(subst ., ,$(CFG_RELEASE_NUM))
+CFG_VERSION_MAJOR := $(word 1,$(CFG_RELEASE_PARTS))
+CFG_VERSION_MINOR := $(word 2,$(CFG_RELEASE_PARTS))
+CFG_VERSION_PATCH := $(word 3,$(CFG_RELEASE_PARTS))
+
 export CFG_VERSION
+export CFG_VERSION_MAJOR
+export CFG_VERSION_MINOR
+export CFG_VERSION_PATCH
+ifneq ($(CFG_PRERELEASE_VERSION),)
+export CFG_PRERELEASE_VERSION
+endif
+ifneq ($(CFG_COMMIT_HASH),)
+export CFG_COMMIT_HASH
+export CFG_COMMIT_DATE
+export CFG_SHORT_COMMIT_HASH
+endif
+export CFG_BUILD_DATE
+export CFG_RELEASE_CHANNEL
 export CFG_DISABLE_CROSS_TESTS
 
 ifeq ($(OS),Windows_NT)
old mode 100644 (file)
new mode 100755 (executable)
index 46daa94..b1e4198
@@ -26,6 +26,7 @@ extern crate toml;
 extern crate url;
 
 use std::env;
+use std::fmt;
 use std::io;
 use rustc_serialize::{Decodable, Encodable};
 use rustc_serialize::json;
@@ -49,6 +50,62 @@ pub mod ops;
 pub mod sources;
 pub mod util;
 
+pub struct CommitInfo {
+    pub short_commit_hash: String,
+    pub commit_hash: String,
+    pub commit_date: String,
+}
+
+pub struct CfgInfo {
+    // Information about the git repository we may have been built from.
+    pub commit_info: Option<CommitInfo>,
+    // The date that the build was performed.
+    pub build_date: String,
+    // The release channel we were built for.
+    pub release_channel: String,
+}
+
+pub struct VersionInfo {
+    pub major: String,
+    pub minor: String,
+    pub patch: String,
+    pub pre_release: Option<String>,
+    // Information that's only available when we were built with
+    // configure/make, rather than cargo itself.
+    pub cfg_info: Option<CfgInfo>,
+}
+
+impl fmt::Display for VersionInfo {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        write!(f, "cargo-{}.{}.{}",
+               self.major, self.minor, self.patch)?;
+        match self.cfg_info.as_ref().map(|ci| &ci.release_channel) {
+            Some(channel) => {
+                if channel != "stable" {
+                    write!(f, "-{}", channel)?;
+                    let empty = String::from("");
+                    write!(f, "{}", self.pre_release.as_ref().unwrap_or(&empty))?;
+                }
+            },
+            None => (),
+        };
+
+        if let Some(ref cfg) = self.cfg_info {
+            match cfg.commit_info {
+                Some(ref ci) => {
+                    write!(f, " ({} {})",
+                           ci.short_commit_hash, ci.commit_date)?;
+                },
+                None => {
+                    write!(f, " (built {})",
+                           cfg.build_date)?;
+                }
+            }
+        };
+        Ok(())
+    }
+}
+
 pub fn execute_main_without_stdin<T, V>(
                                       exec: fn(T, &Config) -> CliResult<Option<V>>,
                                       options_first: bool,
@@ -201,15 +258,47 @@ fn handle_cause(mut cargo_err: &CargoError, shell: &mut MultiShell) -> bool {
     }
 }
 
-pub fn version() -> String {
-    format!("cargo {}", match option_env!("CFG_VERSION") {
-        Some(s) => s.to_string(),
-        None => format!("{}.{}.{}{}",
-                        env!("CARGO_PKG_VERSION_MAJOR"),
-                        env!("CARGO_PKG_VERSION_MINOR"),
-                        env!("CARGO_PKG_VERSION_PATCH"),
-                        option_env!("CARGO_PKG_VERSION_PRE").unwrap_or(""))
-    })
+pub fn version() -> VersionInfo {
+    macro_rules! env_str {
+        ($name:expr) => { env!($name).to_string() }
+    }
+    macro_rules! option_env_str {
+        ($name:expr) => { option_env!($name).map(|s| s.to_string()) }
+    }
+    match option_env!("CFG_RELEASE_CHANNEL") {
+        // We have environment variables set up from configure/make.
+        Some(_) => {
+            let commit_info =
+                option_env!("CFG_COMMIT_HASH").map(|s| {
+                    CommitInfo {
+                        commit_hash: s.to_string(),
+                        short_commit_hash: option_env_str!("CFG_SHORT_COMMIT_HASH").unwrap(),
+                        commit_date: option_env_str!("CFG_COMMIT_DATE").unwrap(),
+                    }
+                });
+            VersionInfo {
+                major: option_env_str!("CFG_VERSION_MAJOR").unwrap(),
+                minor: option_env_str!("CFG_VERSION_MINOR").unwrap(),
+                patch: option_env_str!("CFG_VERSION_PATCH").unwrap(),
+                pre_release: option_env_str!("CFG_PRERELEASE_VERSION"),
+                cfg_info: Some(CfgInfo {
+                    build_date: option_env_str!("CFG_BUILD_DATE").unwrap(),
+                    release_channel: option_env_str!("CFG_RELEASE_CHANNEL").unwrap(),
+                    commit_info: commit_info,
+                }),
+            }
+        },
+        // We are being compiled by Cargo itself.
+        None => {
+            VersionInfo {
+                major: env_str!("CARGO_PKG_VERSION_MAJOR"),
+                minor: env_str!("CARGO_PKG_VERSION_MINOR"),
+                patch: env_str!("CARGO_PKG_VERSION_PATCH"),
+                pre_release: option_env_str!("CARGO_PKG_VERSION_PRE"),
+                cfg_info: None,
+            }
+        }
+    }
 }
 
 fn flags_from_args<T>(usage: &str, args: &[String], options_first: bool) -> CliResult<T>
index f3c8de934155aad898aba5527694fc48bcc5362c..f8ca6dbc40356297ee7cc90bd33cf00056ae81d8 100644 (file)
@@ -225,7 +225,7 @@ pub fn http_handle(config: &Config) -> CargoResult<Easy> {
     handle.connect_timeout(Duration::new(30, 0))?;
     handle.low_speed_limit(10 /* bytes per second */)?;
     handle.low_speed_time(Duration::new(30, 0))?;
-    handle.useragent(&version())?;
+    handle.useragent(&version().to_string())?;
     if let Some(proxy) = http_proxy(config)? {
         handle.proxy(&proxy)?;
     }